package org.ielse.frame.view;

import java.util.ArrayList;
import java.util.List;

import org.ielse.frame.App;
import org.ielse.frame.C;
import org.ielse.frame.R;
import org.ielse.frame.ViewCallback;
import org.ielse.frame.t.volley.ImageLoader;
import org.ielse.frame.util.BaseUtil;
import org.ielse.frame.util.DialogUtil;
import org.ielse.frame.util.DialogUtil.BottomMenuBuidler.BottomMenu;
import org.ielse.frame.util.ImageUtil;
import org.ielse.frame.util.LogUtil;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;

public class PictureLayout extends FrameLayout {

	private long downTime;
	private float downX, downY;
	private float touchSclop;

	private int mWidth;
	private int originalX, originalY, centerX, centerY;
	private boolean finishAnim;
	private boolean pageSingleTouch;

	private TextView txCount;
	private DisableTouchViewPager viewPager;
	private int currPosition;

	private final List<String> urlList = new ArrayList<String>();
	private final List<BottomMenu> menuList = new ArrayList<DialogUtil.BottomMenuBuidler.BottomMenu>();
	private Dialog dlgMenu;

	private ViewCallback viewCallback;

	public PictureLayout(Context context) {
		this(context, null);
	}

	public PictureLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		viewCallback = (ViewCallback) context;
		touchSclop = ViewConfiguration.get(context).getScaledTouchSlop();
		setBackgroundColor(0xff000000);

		viewPager = new DisableTouchViewPager(context);
		viewPager.setOnPageChangeListener(new OnPageChangeListener() {
			private static final int VIEW_STATE = R.id.frame_picture_adapter_item_state;

			class ViewState {
				float translateX, translateY, scaleX, scaleY;
			}

			private boolean isNextChanged = false;
			private PictureView currPicture, nextPicture;
			private int scrollingPosition;

			@Override public void onPageSelected(int position) {
				LogUtil.i("PictureLayout viewPager onPageSelected position:" + position);
				currPosition = position;

				if (urlList.size() > 1) txCount.setText((currPosition + 1) + "/" + urlList.size());
			}

			@Override public void onPageScrolled(int position, float percent, int percentPx) {
				LogUtil.t("PictureLayout viewPager onPageScrolled position:" + position + ",percent:" + percent + ",scrollingPosition"
						+ scrollingPosition);
				if (isNextChanged) {
					isNextChanged = false;

					scrollingPosition = position;

					String currTag = urlList.get(scrollingPosition);
					if (!TextUtils.isEmpty(currTag)) currPicture = (PictureView) viewPager.findViewWithTag(currTag);
					if (currPicture != null) {
						ViewState vs = new ViewState();
						vs.translateX = currPicture.getTranslationX();
						vs.translateY = currPicture.getTranslationY();
						vs.scaleX = currPicture.getScaleX();
						vs.scaleY = currPicture.getScaleY();
						currPicture.setTag(VIEW_STATE, vs);
					}

					if (urlList.size() > scrollingPosition + 1) {
						String nextTag = urlList.get(scrollingPosition + 1);
						if (!TextUtils.isEmpty(nextTag)) nextPicture = (PictureView) viewPager.findViewWithTag(nextTag);
						if (nextPicture != null) {
							ViewState vs = new ViewState();
							vs.translateX = nextPicture.getTranslationX();
							vs.translateY = nextPicture.getTranslationY();
							vs.scaleX = nextPicture.getScaleX();
							vs.scaleY = nextPicture.getScaleY();
							nextPicture.setTag(VIEW_STATE, vs);
						}
					}
				}

				if (position == scrollingPosition && urlList.size() > scrollingPosition + 1) {
					if (percent >= 0 && percent <= 1) {
						if (currPicture != null) {
							ViewState currVs = (ViewState) currPicture.getTag(VIEW_STATE);
							if (currVs.scaleX > 1) currPicture.setScaleX(1 + (currVs.scaleX - 1) * (1 - percent));
							if (currVs.scaleY > 1) currPicture.setScaleY(1 + (currVs.scaleY - 1) * (1 - percent));
							currPicture.setTranslationX(currVs.translateX * (1 - percent));
							currPicture.setTranslationY(currVs.translateY * (1 - percent));
						}

						if (nextPicture != null) {
							ViewState nextVs = (ViewState) nextPicture.getTag(VIEW_STATE);
							if (nextVs.scaleX > 1) nextPicture.setScaleX(1 + (nextVs.scaleX - 1) * percent);
							if (nextVs.scaleY > 1) nextPicture.setScaleY(1 + (nextVs.scaleY - 1) * percent);
							nextPicture.setTranslationX(nextVs.translateX * percent);
							nextPicture.setTranslationY(nextVs.translateY * percent);
						}
					}
				}
			}

			@Override public void onPageScrollStateChanged(int state) {
				LogUtil.i("PictureLayout viewPager onPageScrollStateChanged state:" + state);
				if (state == ViewPager.SCROLL_STATE_SETTLING) {
					isNextChanged = true;
				}
			}
		});

		new ViewPagerScroller(getContext()).initViewPagerScroll(viewPager);
		addView(viewPager);

		menuList.clear();
		menuList.add(new BottomMenu(getContext().getString(R.string.frame_save_photo_to_album), new OnClickListener() {
			@Override public void onClick(View v) {
				if (urlList.size() > currPosition) {
					PictureView viewPicture = (PictureView) viewPager.findViewWithTag(urlList.get(currPosition));
					viewPicture.getTag(1);
					if (viewPicture != null) {
						Bitmap image = ((BitmapDrawable) viewPicture.getDrawable()).getBitmap();
						String pictureName = C.settings.PICTURE_NAME + System.currentTimeMillis() + ".png";
						String picturePath = ImageUtil.saveToSystemAlbum(getContext(), image, pictureName);

						viewCallback.onViewCallback(ViewCallback.ACTION_LAY_PICTURE_SAVE_TO_ALBUM, picturePath);
						dlgMenu.dismiss();
					}
				}
			}
		}));
		dlgMenu = DialogUtil.BottomMenuBuidler.init(getContext()).create(menuList, true, false).setCancelListener(new OnClickListener() {
			@Override public void onClick(View v) {
				dlgMenu.dismiss();
			}
		}).complete();

		ImageView imgMenu = new ImageView(getContext());
		LayoutParams imgMenuLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		imgMenuLp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
		final int spacing = BaseUtil.dipToPx(10);
		imgMenuLp.setMargins(spacing, spacing, spacing, spacing);
		imgMenu.setPadding(spacing, spacing, spacing, spacing);
		imgMenu.setLayoutParams(imgMenuLp);
		imgMenu.setImageResource(R.drawable.frame_picture_menu);
		imgMenu.setAlpha(0.8f);
		imgMenu.setOnClickListener(new OnClickListener() {
			@Override public void onClick(View v) {
				if (!dlgMenu.isShowing()) dlgMenu.show();

			}
		});

		addView(imgMenu);

		txCount = new TextView(getContext());
		LayoutParams txCountLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		txCountLp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
		txCountLp.setMargins(0, spacing, 0, 0);
		txCount.setLayoutParams(txCountLp);
		txCount.setTextColor(Color.WHITE);
		addView(txCount);
	}

	@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mWidth = w;
		centerX = w / 2;
		centerY = h / 2;
	};

	@Override public boolean dispatchTouchEvent(MotionEvent event) {
		if (urlList.size() > currPosition) {
			PictureView viewPicture = (PictureView) viewPager.findViewWithTag(urlList.get(currPosition));
			if (viewPicture != null) viewPicture.dealTouchEvent(event);
		}

		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			LogUtil.i("PictureLayout dispatchTouchEvent ACTION_DOWN");
			downTime = System.currentTimeMillis();
			downX = event.getRawX();
			downY = event.getRawY();
			pageSingleTouch = true;
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			pageSingleTouch = false;
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			LogUtil.i("PictureLayout dispatchTouchEvent ACTION_UP pageSingleTouch:" + pageSingleTouch);
			if (System.currentTimeMillis() - downTime < 1000) {
				if (Math.abs(downX - event.getRawX()) < touchSclop && Math.abs(downY - event.getRawY()) < touchSclop) {
					// fast center clicked
					if (downX > mWidth * 0.2f && downX < mWidth * 0.8f) {
						hiddenPictrue();
						break;
					}
				}
			}

			if (pageSingleTouch) {
				if (downX < mWidth * 0.1f) {
					float upX = event.getRawX();
					if (upX > mWidth * 0.1f && currPosition > 0) {
						LogUtil.e("viewPager.setCurrentItem(currPosition - 1);" + (currPosition - 1));
						viewPager.setCurrentItem(currPosition - 1);
					}
				}
				else if (downX > mWidth * 0.9f) {
					float upX = event.getRawX();
					if (upX < mWidth * 0.9f && currPosition < urlList.size() - 1) {
						LogUtil.e("viewPager.setCurrentItem(currPosition + 1);" + (currPosition + 1));
						viewPager.setCurrentItem(currPosition + 1);
					}
				}
			}

			break;
		}

		return super.dispatchTouchEvent(event);
	}

	@Override public boolean onTouchEvent(MotionEvent event) {
		return MotionEvent.ACTION_DOWN == event.getAction() ? true : super.onTouchEvent(event);
	}

	public void showPicture(String url, View original) {
		List<String> mUrlList = new ArrayList<String>();
		mUrlList.add(url);
		showPicture(mUrlList, original);
	}

	public void showPicture(List<String> mUrlList, View original) {
		showPicture(mUrlList, 0, original);
	}

	public void showPicture(List<String> mUrlList, int position, View original) {
		if (mUrlList == null || mUrlList.size() < 1) throw new IllegalArgumentException("empty img url");
		String url = mUrlList.get(position);
		if (TextUtils.isEmpty(url)) throw new IllegalArgumentException("empty img url");

		urlList.clear();
		urlList.addAll(mUrlList);
		currPosition = position;

		viewPager.setAdapter(adapter);
		viewPager.setCurrentItem(currPosition);
		if (urlList.size() > 1) txCount.setText((currPosition + 1) + "/" + urlList.size());

		int[] location = new int[2];
		original.getLocationOnScreen(location);
		originalX = location[0];
		originalY = location[1] - BaseUtil.getStatusBarHeight();

		setAlpha(0);
		setVisibility(View.VISIBLE);

		post(new Runnable() {
			@Override public void run() {
				if (urlList.size() > currPosition) {
					String initUrl = urlList.get(currPosition);
					if (!TextUtils.isEmpty(initUrl)) {
						PictureView viewPicture = (PictureView) viewPager.findViewWithTag(initUrl);
						if (viewPicture != null) {
							if (originalX != 0 || originalY != 0) {
								viewPicture.setTranslationX(originalX - centerX);
								viewPicture.setTranslationY(originalY - centerY);
							}
							ObjectAnimator anim1 = ObjectAnimator.ofFloat(viewPicture, "translationX", originalX - centerX, 0);
							ObjectAnimator anim2 = ObjectAnimator.ofFloat(viewPicture, "translationY", originalY - centerY, 0);
							ObjectAnimator anim3 = ObjectAnimator.ofFloat(viewPicture, "scaleX", 1, 2);
							ObjectAnimator anim4 = ObjectAnimator.ofFloat(viewPicture, "scaleY", 1, 2);
							ObjectAnimator anim5 = ObjectAnimator.ofFloat(viewPicture, "rotation", 0, 360);
							ObjectAnimator anim6 = ObjectAnimator.ofFloat(PictureLayout.this, "alpha", 0, 1);

							AnimatorSet set = new AnimatorSet();
							set.play(anim1).with(anim2).with(anim3).with(anim4).with(anim5).with(anim6);
							set.setInterpolator(new DecelerateInterpolator());
							set.setDuration(600);
							set.start();
							set.addListener(new AnimatorListenerAdapter() {
								@Override public void onAnimationEnd(Animator animation) {
									super.onAnimationEnd(animation);
								}
							});
						}
					}
				}
			}
		});
	}

	public void hiddenPictrue() {
		if (urlList.size() > currPosition) {
			if (finishAnim) return;
			finishAnim = true;

			PictureView viewPicture = (PictureView) viewPager.findViewWithTag(urlList.get(currPosition));
			if (viewPicture == null) return;

			ObjectAnimator anim1 = ObjectAnimator.ofFloat(viewPicture, "translationX", 0, originalX - centerX);
			ObjectAnimator anim2 = ObjectAnimator.ofFloat(viewPicture, "translationY", 0, originalY - centerY);
			ObjectAnimator anim3 = ObjectAnimator.ofFloat(viewPicture, "scaleX", viewPicture.getScaleX(), 1);
			ObjectAnimator anim4 = ObjectAnimator.ofFloat(viewPicture, "scaleY", viewPicture.getScaleY(), 1);
			ObjectAnimator anim5 = ObjectAnimator.ofFloat(viewPicture, "rotation", 360, 0);
			ObjectAnimator anim6 = ObjectAnimator.ofFloat(this, "alpha", 1, 0);
			anim5.setInterpolator(new DecelerateInterpolator());

			AnimatorSet set = new AnimatorSet();
			set.play(anim1).with(anim2).with(anim3).with(anim4).with(anim5).with(anim6);
			set.setDuration(600);
			set.start();
			set.addListener(new AnimatorListenerAdapter() {
				@Override public void onAnimationEnd(Animator animation) {
					super.onAnimationEnd(animation);
					setVisibility(View.GONE);
					setAlpha(1);
					finishAnim = false;
				}
			});

			urlList.clear();
			adapter.notifyDataSetChanged();
		}
	}

	private PagerAdapter adapter = new PagerAdapter() {
		@Override public Object instantiateItem(ViewGroup container, int position) {
			PictureView viewPicture = new PictureView(getContext());
			final String url = urlList.get(position);
			viewPicture.setTag(url);
			viewPicture.setScaleType(ScaleType.CENTER);

			App.i()
					.getImageLoader()
					.get(url,
							ImageLoader.getImageListener(viewPicture, R.drawable.test_img_default, R.drawable.test_img_loading,
									R.drawable.test_img_fail));

			container.addView(viewPicture);
			return viewPicture;
		};

		@Override public void destroyItem(ViewGroup container, int position, Object object) {
			if (urlList.size() > position) {
				PictureView viewPictrue = (PictureView) container.findViewWithTag(urlList.get(position));
				if (viewPictrue != null) container.removeView(viewPictrue);
			}
		}

		@Override public boolean isViewFromObject(View arg0, Object arg1) {
			return arg0 == arg1;
		}

		@Override public int getCount() {
			return urlList.size();
		}
	};

	public class PictureView extends ImageView {
		private int imageWidth, imageHeight;
		private int mWidth, mHeight;
		private int scaleRatioReferWidth;
		private static final float MIN_MIN = 0.5f;
		private static final float MIN_NORMAL = 1f;
		private static final float MAX_MAX = 6f;
		private static final float MAX_NORMAL = 4f;
		private float downX, downY;
		private double downDist;
		private float oldScaleX, oldScaleY;
		private float oldTranslationX, oldTranslationY;
		private float fingersCenterX, fingersCenterY;
		private boolean scaleMode;
		private boolean anim;

		public PictureView(Context context) {
			this(context, null);
		}

		public PictureView(Context context, AttributeSet attrs) {
			super(context, attrs);
		}

		@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
			super.onSizeChanged(w, h, oldw, oldh);
			LogUtil.i("PictureView onSizeChanged width:" + w + ",height:" + h);

			mWidth = w;
			mHeight = h;

			scaleRatioReferWidth = w / 10;
		}

		@Override public void setImageBitmap(Bitmap bm) {
			super.setImageBitmap(bm);
			LogUtil.i("PictureView setImageBitmap width:" + bm.getWidth() + ",height:" + bm.getHeight());
			imageWidth = bm.getWidth();
			imageHeight = bm.getHeight();
		}

		public void dealTouchEvent(MotionEvent event) {
			if (!anim) {
				switch (event.getAction() & MotionEvent.ACTION_MASK) {
				case MotionEvent.ACTION_DOWN:
					downX = event.getRawX();
					downY = event.getRawY();
					oldTranslationX = getTranslationX();
					oldTranslationY = getTranslationY();
					oldScaleX = getScaleX();
					oldScaleY = getScaleY();
					break;
				case MotionEvent.ACTION_POINTER_DOWN:
					downDist = calcDistanceBetweenFingers(event);
					downX = event.getRawX();
					downY = event.getRawY();
					oldTranslationX = getTranslationX();
					oldTranslationY = getTranslationY();
					oldScaleX = getScaleX();
					oldScaleY = getScaleY();
					float[] downResult = calcCenterPointBetweenFingers(event);
					fingersCenterX = downResult[0];
					fingersCenterY = downResult[1];
					break;
				case MotionEvent.ACTION_MOVE:
					if (downX == 0 && downY == 0) {
						downX = event.getRawX();
						downY = event.getRawY();
						oldTranslationX = getTranslationX();
						oldTranslationY = getTranslationY();
						oldScaleX = getScaleX();
						oldScaleY = getScaleY();
					}

					if (event.getPointerCount() > 1) {
						scaleMode = true;
						double moveDist = calcDistanceBetweenFingers(event);
						if (moveDist != downDist) {
							float changedScaleSize = (float) ((moveDist - downDist) / scaleRatioReferWidth);
							scalePicture(changedScaleSize);
						}
						float[] moveResult = calcCenterPointBetweenFingers(event);
						if (fingersCenterX != moveResult[0] && fingersCenterY != moveResult[1]) {
							final float moveX = moveResult[0] - fingersCenterX;
							final float moveY = moveResult[1] - fingersCenterY;
							translatePicture(moveX, moveY);
						}
					}
					else if (event.getPointerCount() == 1 && !scaleMode) {
						final float moveX = event.getRawX() - downX;
						final float moveY = event.getRawY() - downY;
						translatePicture(moveX, moveY);
					}
					break;
				case MotionEvent.ACTION_CANCEL:
				case MotionEvent.ACTION_UP:
					recoverPictureByAnim();
					break;
				}
			}
		}

		private void translatePicture(float x, float y) {
			setTranslationX(oldTranslationX + x * oldScaleX);
			setTranslationY(oldTranslationY + y * oldScaleY);
		}

		private void scalePicture(float changedScaleSize) {
			if (oldScaleX + changedScaleSize > MIN_MIN && oldScaleX + changedScaleSize < MAX_MAX) {
				setScaleX(oldScaleX + changedScaleSize);
			}
			if (oldScaleY + changedScaleSize > MIN_MIN && oldScaleY + changedScaleSize < MAX_MAX) {
				setScaleY(oldScaleY + changedScaleSize);
			}
		}

		private void recoverPictureParameters() {
			downX = 0;
			downY = 0;
			downDist = 0;
			scaleMode = false;
			fingersCenterX = 0;
			fingersCenterY = 0;
		}

		private void recoverPictureByAnim() {

			recoverPictureParameters();

			final float scaleX = getScaleX();
			final float scaleY = getScaleY();
			final float translationX = getTranslationX();
			final float translationY = getTranslationY();

			final float transOffsetX = (imageWidth * scaleX - mWidth) / 2;
			ObjectAnimator anim1 = null;
			if (transOffsetX < 0) {
				anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, 0);
			}
			else if (translationX > transOffsetX) {
				anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, transOffsetX);
			}
			else if (translationX < -transOffsetX) {
				anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, -transOffsetX);
			}
			final float transOffsetY = (imageHeight * scaleY - mHeight) / 2;
			ObjectAnimator anim2 = null;
			if (transOffsetY < 0) {
				anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, 0);
			}
			else if (translationY > transOffsetY) {
				anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, transOffsetY);
			}
			else if (translationY < -transOffsetY) {
				anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, -transOffsetY);
			}
			ObjectAnimator anim3 = null;
			if (scaleX < MIN_NORMAL) {
				anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MIN_NORMAL);
			}
			else if (scaleX > MAX_NORMAL) {
				anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MAX_NORMAL);
			}
			ObjectAnimator anim4 = null;
			if (scaleY < MIN_NORMAL) {
				anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MIN_NORMAL);
			}
			else if (scaleY > MAX_NORMAL) {
				anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MAX_NORMAL);
			}

			AnimatorSet set = new AnimatorSet();
			AnimatorSet.Builder b = set.play(ObjectAnimator.ofInt(0));
			if (anim1 != null) b.with(anim1);
			if (anim2 != null) b.with(anim2);
			if (anim3 != null) b.with(anim3);
			if (anim4 != null) b.with(anim4);

			set.setInterpolator(new DecelerateInterpolator());
			set.setDuration(400);
			set.start();
			set.addListener(new AnimatorListenerAdapter() {
				@Override public void onAnimationStart(Animator animation) {
					super.onAnimationStart(animation);
					anim = true;
				}

				@Override public void onAnimationEnd(Animator animation) {
					super.onAnimationEnd(animation);
					anim = false;
				}
			});
		}

		private double calcDistanceBetweenFingers(MotionEvent event) {
			float disX = Math.abs(event.getX(0) - event.getX(1));
			float disY = Math.abs(event.getY(0) - event.getY(1));
			return Math.sqrt(disX * disX + disY * disY);
		}

		private float[] calcCenterPointBetweenFingers(MotionEvent event) {
			float xPoint0 = event.getX(0);
			float yPoint0 = event.getY(0);
			float xPoint1 = event.getX(1);
			float yPoint1 = event.getY(1);
			return new float[] { (xPoint0 + xPoint1) / 2, (yPoint0 + yPoint1) / 2 };
		}
	}

	class DisableTouchViewPager extends ViewPager {
		public DisableTouchViewPager(Context context) {
			super(context);
		}

		@Override public boolean onInterceptTouchEvent(MotionEvent arg0) {
			return false;
		}

		@Override public boolean onTouchEvent(MotionEvent arg0) {
			return false;
		}
	}

}
